" Header的公共代码 {{{
let s:header_marks_pattern = '\v^(\=+|\-+)\s*$'
let s:header_content_pattern = '\v(^[^*]*)(\*[^*]*\*\s*)*$'

" 判断一行是不是Header行：
"
" - 0：不是Header行
" - 1：Header Marks行
" - 2：Header Content行
function! s:IsHeaderLine(line_no)
    if getline(a:line_no) =~ s:header_marks_pattern
        return 1
    elseif getline(a:line_no - 1) =~ s:header_marks_pattern && !std#settings#IsModeline(a:line_no)
        return 2
    else
        return 0
    endif
endfunction

" 从单行文本中提取出title和tags
function! s:ExtractHeaderFromLine(line_text)
    " 获取title部分
    let matched = matchstr(a:line_text, '\v^[^*]*\s*')
    let title = std#string#Trim(matched)

    " 获取tags部分
    let start = strlen(matched)
    let tags = []
    while start < strlen(a:line_text)
        let matched = matchstr(a:line_text, '\v\*[^*]*\*\s*', start)
        call add(tags, std#string#Trim(matched)[1:-2])
        let start += strlen(matched)
    endwhile

    return { 'title': title, 'tags': tags }
endfunction
" }}}

" 格式化Header的代码。 {{{

" 如果遇到的是`====`或`----`，则该行格式化为相应长度的`=`或`-`号。
" 如果遇到的是`====`或`----`，下面的部分，则将`title *tag1* *tag2*`对齐到两边。

function! ft#help#FormatHeader(does_notify_error)
    let is_header_line = s:IsHeaderLine(line('.'))
    if is_header_line == 1
        call s:FormatHeaderMarks()
    elseif is_header_line == 2
        call s:FormatHeaderContent()
    elseif a:does_notify_error
        call std#messages#EchoError('非header部分')
    endif
endfunction

function! s:FormatHeaderMarks()
    let mark = getline('.')[0]
    let marks = map(range(1, &textwidth), '"' . mark . '"')
    let line = join(marks, '')
    call setline('.', line)
endfunction

function! s:FormatHeaderContent()
    let current_line = getline('.')
    if current_line !~ s:header_content_pattern
        call std#messages#EchoError('错误的格式')
        return
    endif

    let header_line = getline(line('.') - 1)
    let header_width = strdisplaywidth(header_line)
    let header_dict = s:ExtractHeaderFromLine(current_line)

    " 调整格式
    if empty(header_dict.tags)
        call setline('.', header_dict.title)
    else
        let title_part = header_dict.title
        let tags_part = join(map(header_dict.tags, '"*" . v:val . "*"'), ' ')
        let text_width = header_width - strdisplaywidth(title_part) - strdisplaywidth(tags_part)
        let seprator_length = max([text_width, 1])
        let seprator_part = join(map(range(1, seprator_length), '" "'), '')
        let line = title_part . seprator_part . tags_part
        call setline('.', line)
    endif
endfunction
" }}}

" 处理特殊符号的代码，如`>`、`*`、`|`等。 {{{

" 显示特殊符号
function! ft#help#ShowMarks()
    setlocal conceallevel=0
    hi link helpIgnore Normal
    hi link helpBar Normal
    hi link helpBacktick Normal
    hi link helpStar Normal
endfunction

" 隐藏特殊符号
function! ft#help#HideMarks()
    setlocal conceallevel=2
    hi link helpIgnore Ignore
    hi link helpBar Ignore
    hi link helpBacktick Ignore
    hi link helpStar Ignore
endfunction

" Toggle特殊符号
function! ft#help#ToggleMarks()
    if &conceallevel == 0
        " Shown
        call ft#help#HideMarks()
    else
        " Hidden
        call ft#help#ShowMarks()
    endif
endfunction
" }}}

" 插入目录的代码。 {{{
function! ft#help#InsertContents()
    if getline('.') !~ '\v^\s*$'
        call std#messages#EchoError('只允许在空白处插入目录')
        return
    endif

    " 希望下面是一行空白
    if getline(line('.') + 1) !~ '\v^\s*$'
        if getline(line('.') - 1) =~ '\v^\s*$'
            normal! k
        else
            call append('.', '')
        endif
    endif
    
    let lines = s:GenerateContents()
    if empty(lines)
        call std#messages#EchoError('未提取到Contents')
    else
        call append('.', lines)
        normal! j
    endif
endfunction

function! ft#help#UpdateContents()
    if getline('.') =~ '\v^\s*$'
        call std#messages#EchoError('不允许在空白处更新目录')
        return
    endif

    normal! dip
    call ft#help#InsertContents()
endfunction

" 根据光标位置所在的目录，调整接下来的文章的层次。
"
" 调整的位置从第一次遇到的`====`或`----`开始，直至文章结尾。
" 对于在目录中的Section，按照目录的顺序重新排列。对于不在目录中的部分，依照原顺序排在末尾。
function! ft#help#AdjustToContents()
    let contents = s:ReadContents()

    " 定位到第一个header marks处
    let started_at = search(s:header_marks_pattern, 'W')
    if started_at == 0
        echom '无内容可更新'
        return
    endif

    let sections = s:ReadSections()

    let title_indexes = {}
    for i in range(0, len(contents) - 1)
        let title = contents[i].title
        let title_indexes[title] = i
    endfor

    let sections_in_contents = map(contents, "{ 'title': v:val.title, 'text': [] }")
    let sections_not_in_contents = []
    for section in sections
        let title = section.title
        if has_key(title_indexes, title)
            let index = title_indexes[title]
            let sections_in_contents[index].text = section.text
        else
            call add(sections_not_in_contents, section)
        endif
    endfor

    let ranked_sections = sections_in_contents + sections_not_in_contents
    call cursor(started_at, 0)
    let stored_register_content = @"
    silent normal dG
    let @" = stored_register_content
    for section in ranked_sections
        call append('$', section.text)
    endfor

    echom '章节调整完成'
endfunction

function! s:GenerateContents()
    let lines = []
    let headers = s:ExtractHeaders(line('.'), line('$'))
    for header in headers
        let start_part = header.mark == '=' ? '' : '    '
        let title_part = header.title
        let tags_part = join(map(header.tags, '"|" . v:val . "|"'), ' ')
        let seprator_length = &textwidth - strdisplaywidth(start_part) - strdisplaywidth(title_part) - strdisplaywidth(tags_part) 
        let seprator_length = max([seprator_length, 1])
        let seprator_part = join(map(range(1, seprator_length), '" "'), '')
        let line = start_part . title_part . seprator_part . tags_part
        call add(lines, line)
    endfor

    return lines
endfunction

" 读取光标所在位置的目录的标题
function! s:ReadContents()
    let stored_register_content = @"
    silent normal yip
    let conents_text = @"
    let @" = stored_register_content

    let contents = []
    let contents_lines = split(conents_text, "\n")
    for contents_line in contents_lines
        let title = std#string#Trim(matchstr(contents_line, '\v^[^|]*'))
        call add(contents, { 'title': title })
    endfor
    return contents
endfunction

" 已经定位到第一次的header处了，读取接下来的sections.
function! s:ReadSections()
    let sections = []
    let current = line('.')
    while current <= line('$')
        let next = search(s:header_marks_pattern, 'W')
        if next == 0
            let next = line('$') + 1
        endif
        let text = getline(current, next - 1)
        let header = s:ExtractHeaderFromLine(text[1])
        call add(sections, { 'title': header.title, 'text': text })
        let current = next
    endwhile
    return sections
endfunction

" 从当前Buffer的指定范围内提取出Header行的title、tags和使用的mark
function! s:ExtractHeaders(start, end)
    let headers = []
    let previous_line = getline(a:start - 1)
    for i in range(a:start, a:end)
        let current_line = getline(i)
        if s:IsHeaderLine(i) == 2 && current_line !~ '\v^\s+'
            let header = s:ExtractHeaderFromLine(current_line)
            let header.mark = previous_line[0]
            call add(headers, header)
        endif
        let previous_line = current_line
    endfor
    return headers
endfunction
" }}}

